import logging
from google.appengine.ext import db

# Model class
MAX_FETCH = 1000

class Budget(db.Model):
    year = db.IntegerProperty()
    name = db.StringProperty()
    owner_id = db.StringProperty()
    owner = db.UserProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)

    def total_by_category_month(self):
        categoryService = CategoryService()

        summary = [['Categorias','Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']]
        categories = categoryService.get_all(self.owner)
        for category in categories:
            month_1 = 0
            month_2 = 0
            month_3 = 0
            month_4 = 0
            month_5 = 0
            month_6 = 0
            month_7 = 0
            month_8 = 0
            month_9 = 0
            month_10 = 0
            month_11 = 0
            month_12 = 0
            
            total = category.total_by_moth()
            if len(total) > 0:
                for x in range(0,len(total)):
                    if x == 0:
                        month_1 = total[x][1]
                    elif x ==1:
                        month_2 = total[x][1]
                    elif x ==2:
                        month_3 = total[x][1]
                    elif x ==3:
                        month_4 = total[x][1]
                    elif x ==4:
                        month_5 = total[x][1]
                    elif x ==5:
                        month_6 = total[x][1]
                    elif x ==6:
                        month_7 = total[x][1]
                    elif x ==7:
                        month_8 = total[x][1]
                    elif x ==8:
                        month_9 = total[x][1]
                    elif x ==9:
                        month_10 = total[x][1]
                    elif x ==10:
                        month_11 = total[x][1]
                    elif x ==11:
                        month_12 = total[x][1]
                summary.append([category.name,month_1,month_2,month_3,month_4,month_5,month_6,month_7,month_8,month_8,month_10,month_11,month_12])

        return summary

class Month(db.Model):
    number = db.IntegerProperty()
    name = db.StringProperty()
    budget = db.ReferenceProperty(Budget,collection_name='months')
    income = db.FloatProperty()
    extra_income = db.FloatProperty()
    owner_id = db.StringProperty()
    owner = db.UserProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)

    def total_income(self):
        return self.income + self.extra_income

    def total_expense(self):
        total = 0
        for expense in self.expenses:
            total += expense.amount
        return total

    def balansheet(self):
        return self.total_income() - self.total_expense()

    def total_by_category(self):
        total = {}
        for expense in self.expenses:
            if str(expense.category.name) in total:
                current_value = total[str(expense.category.name)]
            else:
                current_value = 0.0

            total[str(expense.category.name)] = current_value + expense.amount

        our_list = total.items() 
        our_list.sort()
        return our_list
    
    def total_by_type(self):
        total = {'fijo':0,'variable':0}
        for expense in self.expenses:
            current_value = total[expense.category.type]
            total[expense.category.type] = current_value + expense.amount

        our_list = total.items() 
        our_list.sort()
        return our_list
    
class Category(db.Model):
    name = db.StringProperty()
    type = db.StringProperty(choices=['fijo','variable'])
    owner_id = db.StringProperty()
    owner = db.UserProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    def total_by_moth(self):
        total = {}
        for expense in self.expenses:
            if int(expense.month.number) in total:
                current_value = total[int(expense.month.number)]
            else:
                current_value = 0.0

            total[int(expense.month.number)] = current_value + expense.amount

        our_list = total.items() 
        our_list.sort()
        return our_list

class Expense(db.Model):
    description = db.StringProperty()
    amount = db.FloatProperty()
    category = db.ReferenceProperty(Category,collection_name='expenses')
    month = db.ReferenceProperty(Month,collection_name='expenses')
    comment = db.StringProperty()
    owner_id = db.StringProperty()
    owner = db.UserProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)

# Services Class
class BudgetService:

    def initialize(self,year,user):
        budgetService = BudgetService()
        categoryService = CategoryService()
        monthService = MonthService()
        expenseService = ExpenseService()

        logging.info("Creating budget first time...")
        budget = Budget()
        budget.year = year
        budget.name = "Gastos - " + str(year)
        budgetService.save(budget,user)

    def get_by_id(self,id,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_id'
        logging.info('%s: %s: %s' %(clsname,fncname,str(id)))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        budget = Budget.get_by_id(id)

        logging.info('%s: %s: validating user access....' %(clsname,fncname))
        if budget.owner_id != user.user_id():
            logging.error('%s: %s: invalid user %s!' %(clsname,fncname,user.nickname))
            raise Exception("the entity does not exist!")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return budget

    def get_by_year(self,year,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_year'
        logging.info('%s: %s: %s' %(clsname,fncname,year))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Budget.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.filter("year = ",year)

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.get()

    def get_all(self,user):
        clsname = self.__class__.__name__
        fncname = 'get_all'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Budget.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.order("name")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.fetch(MAX_FETCH)

    def save(self,budget,user):
        clsname = self.__class__.__name__
        fncname = 'save'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: validating duplicated name' %(clsname,fncname))
        dup_key = self.get_by_year(budget.year,user)
        if (dup_key != None) and (dup_key.key().id() != budget.key().id()):
            logging.error('%s: %s: Duplicated key %s!' %(clsname,fncname,budget.year))
            raise Exception('Duplicated key %s' % (budget.year))

        new_budget = False
        if budget.is_saved():
            logging.info('%s: %s: update %s' %(clsname,fncname,budget.year))
            _budget = self.get_by_id(budget.key().id(),user)
        else:
            logging.info('%s: %s: insert %s' %(clsname,fncname,budget.year))
            _budget = Budget()
            new_budget = True

        logging.info('%s: %s: saving enity...' %(clsname,fncname))            
        _budget.year = budget.year
        _budget.name = budget.name
        _budget.owner_id = user.user_id()
        _budget.owner = user
        _budget.put()

        logging.info('%s: %s: saved successfully!' %(clsname,fncname))

        if new_budget:
            logging.info('%s: %s: creating associated month' %(clsname,fncname))
            ms = MonthService()
            i = 1
            meses = ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre']
            for mes in meses:
                m = Month()
                m.budget = _budget
                m.number = i
                m.name = mes
                m.income = 0.0
                m.extra_income = 0.0
                ms.save(m,user)
                i += 1            
        
        logging.info('%s: %s: saved successfully!' %(clsname,fncname))
        return _budget

    def delete(self,budget,user):
        clsname = self.__class__.__name__
        fncname = 'delete'
        logging.info('%s: %s: %s' %(clsname,fncname,budget.year))
        
        ms = MonthService()
        logging.info('%s: %s: deleting associated months' %(clsname,fncname))
        for month in budget.months:
            logging.info('%s: %s: deleting associated month %s - %s' % (clsname,fncname,budget.year,month.name))
            ms.delete(month,user)

        budget.delete()
        logging.info('%s: %s: deleted successfully!' %(clsname,fncname))

class MonthService:
    def get_by_id(self,id,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_id'
        logging.info('%s: %s: %s' %(clsname,fncname,str(id)))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        month = Month.get_by_id(id)

        logging.info('%s: %s: validating user access....' %(clsname,fncname))
        if month.owner_id != user.user_id():
            logging.error('%s: %s: invalid user %s!' %(clsname,fncname,user.nickname))
            raise Exception("the entity does not exist!")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return month

    def get_by_number(self,number,year,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_number'
        logging.info('%s: %s: %s-%s' %(clsname,fncname,number,year))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        bs = BudgetService()
        budget = bs.get_by_year(year,user)
        month = Month.gql("WHERE number = :1 AND budget = :2 AND owner_id = :3", number,budget,user.user_id()).get()

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return month

    def get_all(self,user):
        clsname = self.__class__.__name__
        fncname = 'get_all'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Month.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.order("number")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.fetch(MAX_FETCH)

    def save(self,month,user):
        clsname = self.__class__.__name__
        fncname = 'save'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: validating duplicated name' %(clsname,fncname))
        dup_key = self.get_by_number(month.number,month.budget.year,user)
        if (dup_key != None) and (dup_key.key().id() != month.key().id()):
            logging.error('%s: %s: Duplicated key %s!' %(clsname,fncname,month.number))
            raise Exception('Duplicated key %s' % (month.number))

        if month.is_saved():
            logging.info('%s: %s: update %s' %(clsname,fncname,month.number))
            _month = self.get_by_id(month.key().id(),user)
        else:
            logging.info('%s: %s: insert %s' %(clsname,fncname,month.number))
            _month = Month()
            
        logging.info('%s: %s: saving enity...' %(clsname,fncname))            
        _month.number = month.number
        _month.name = month.name
        _month.budget = month.budget
        _month.income = month.income
        _month.extra_income = month.extra_income
        _month.owner_id = user.user_id()
        _month.owner = user
        _month.put()
        return _month

    def delete(self,month,user):
        clsname = self.__class__.__name__
        fncname = 'delete'
        logging.info('%s: %s: %s' %(clsname,fncname,month.name))

        logging.info('%s: %s: deleting associated expenses' %(clsname,fncname))
        expenseService = ExpenseService()
        for expense in month.expenses:
            logging.info('%s: %s: deleting associated expense %s - %s' % (clsname,fncname,month.number,expense.description))
            expenseService.delete(expense,user)

        month.delete()
        logging.info('%s: %s: deleted successfully!' %(clsname,fncname))

class CategoryService:
       
    def get_by_id(self,id,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_id'
        logging.info('%s: %s: %s' %(clsname,fncname,str(id)))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        category = Category.get_by_id(id)

        logging.info('%s: %s: validating user access....' %(clsname,fncname))
        if category.owner_id != user.user_id():
            logging.error('%s: %s: invalid user %s!' %(clsname,fncname,user.nickname))
            raise Exception("the entity does not exist!")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return category

    def get_by_name(self,name,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_name'
        logging.info('%s: %s: %s' %(clsname,fncname,name))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Category.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.filter("name = ",name)

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.get()

    def get_all(self,user):
        clsname = self.__class__.__name__
        fncname = 'get_all'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Category.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.order("name")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.fetch(MAX_FETCH)

    def save(self,category,user):
        clsname = self.__class__.__name__
        fncname = 'save'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: validating duplicated name' %(clsname,fncname))
        dup_key = self.get_by_name(category.name,user)
        if (dup_key != None) and (dup_key.key().id() != category.key().id()):
            logging.error('%s: %s: Duplicated key %s!' %(clsname,fncname,category.name))
            raise Exception('Duplicated key %s' % (category.name))

        if category.is_saved():
            logging.info('%s: %s: update %s' %(clsname,fncname,category.name))
            _category = self.get_by_id(category.key().id(),user)
        else:
            logging.info('%s: %s: insert %s' %(clsname,fncname,category.name))
            _category = Category()

        logging.info('%s: %s: saving enity...' %(clsname,fncname))            
        _category.name = category.name
        _category.type = category.type
        _category.owner_id = user.user_id()
        _category.owner = user
        _category.put()

        logging.info('%s: %s: saved successfully!' %(clsname,fncname))
        return _category

    def delete(self,category,user):
        clsname = self.__class__.__name__
        fncname = 'delete'
        logging.info('%s: %s: %s' %(clsname,fncname,category.name))

        logging.info('%s: %s: validating if the category has been used' %(clsname,fncname))
        for expense in category.expenses:
            logging.error('%s: %s: cannot delete category %s because it has associated expenses!' %(clsname,fncname,category.name))
            raise Exception('cannot delete category %s because it has associated expenses!' % (category.name))

        category.delete()    
        logging.info('%s: %s: deleted successfully!' %(clsname,fncname))
        
class ExpenseService:
    def get_by_id(self,id,user):
        clsname = self.__class__.__name__
        fncname = 'get_by_id'
        logging.info('%s: %s: %s' %(clsname,fncname,str(id)))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        expense = Expense.get_by_id(id)

        logging.info('%s: %s: validating user access....' %(clsname,fncname))
        if expense.owner_id != user.user_id():
            logging.error('%s: %s: invalid user %s!' %(clsname,fncname,user.nickname))
            raise Exception("the entity does not exist!")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return expense

    def get_all(self):
        clsname = self.__class__.__name__
        fncname = 'get_all'
        logging.info('%s: %s ' %(clsname,fncname))

        logging.info('%s: %s: retriving entity..' %(clsname,fncname))
        qry = Expense.all()
        qry.filter("owner_id = ",user.user_id())    #security filter
        qry.order("description")

        logging.info('%s: %s: return OK!' %(clsname,fncname))
        return qry.fetch(MAX_FETCH)

    def save(self,expense,user):
        clsname = self.__class__.__name__
        fncname = 'save'
        logging.info('%s: %s ' %(clsname,fncname))

        if expense.is_saved():
            logging.info('%s: %s: update %s' %(clsname,fncname,expense.description))
            _expense = self.get_by_id(expense.key().id(),user)
        else:
            logging.info('%s: %s: insert %s' %(clsname,fncname,expense.description))
            _expense = Expense()
            
        logging.info('%s: %s: saving enity...' %(clsname,fncname))
        _expense.description = expense.description
        _expense.amount = expense.amount
        _expense.category = expense.category
        _expense.month = expense.month
        _expense.comment = expense.comment
        _expense.owner_id = user.user_id()
        _expense.owner = user
        _expense.put()

        logging.info('%s: %s: saved successfully!' %(clsname,fncname))        
        return _expense

    def delete(self,expense,user):
        clsname = self.__class__.__name__
        fncname = 'delete'
        logging.info('%s: %s: %s' %(clsname,fncname,expense.description))

        expense.delete()
        logging.info('%s: %s: deleted successfully!' %(clsname,fncname))        
